#pragma once

#include <sstream>
#include "bencode.hpp"

namespace alpha { namespace rpc {

template <typename Stream, template <typename> class Tmpl>
Stream & operator >>(Stream &stream, basic_bencode<Tmpl> &r)
{
	typename Stream::int_type ch = stream.get();
	if (!stream.good())
		return stream;
	switch (ch)
	{
		case 'i':
		{
			// read until 'e'
			std::stringstream ss;
			if (!stream.get(*ss.rdbuf(), 'e').good())
				return stream;
			// skip 'e'
			stream.get();
			// convert the string into an integer
			typedef typename basic_bencode<Tmpl>::integer integer;
			r = integer();
			integer &v = r.template reference<integer>();
			ss >> std::noskipws >> v;
			if (ss.fail() || !ss.eof())
			{
				stream.setstate(std::ios::failbit);
				return stream;
			}
			// assign
			r = v;
			break;
		}
		case 'l':
		{
			typedef typename basic_bencode<Tmpl>::list list;
			r = list();
			list &v = r.template reference<list>();
			for (; stream.peek() != 'e'; )
			{
				basic_bencode<Tmpl> sub;
				if (!(stream >> sub).good())
					return stream;
				v.push_back(sub);
			}
			// skip the last 'e'
			stream.get();
			break;
		}
		case 'd':
		{
			typedef typename basic_bencode<Tmpl>::dictionary dictionary;
			r = dictionary();
			dictionary &v = r.template reference<dictionary>();
			for (; stream.peek() != 'e';)
			{
				basic_bencode<Tmpl> key, mapped;
				stream >> key >> mapped;
				if (!stream.good())
					return stream;
				typedef typename dictionary::key_type key_type;
				key_type *pkey = key.template pointer<key_type>();
				if (pkey == 0)
				{
					// check the key_type of dictionaries
					stream.setstate(std::ios::failbit);
					return stream;
				}
				//v.insert(dictionary::value_type(*pkey, mapped));
				v[*pkey] = mapped;
			}
			// skip the last 'e'
			stream.get();
			break;
		}
		default:
		{
			std::stringstream ss;
			ss.put(ch);
			if (stream.peek() != ':') // avoid zero-extraction failure
			{
				if (!stream.get(*ss.rdbuf(), ':').good())
					return stream;
			}
			// skip ':'
			stream.get();
			// convert the string into an integer
			std::streamsize size;
			ss >> std::noskipws >> size;
			if (ss.fail() || !ss.eof())
			{
				stream.setstate(std::ios::failbit);
				return stream;
			}
			// read the content of the string
			typedef typename basic_bencode<Tmpl>::string string;
			r = string();
			string &v = r.template reference<string>();
			v.resize(size);
			if (!stream.read(&v[0], size).good())
				return stream;
			break;
		}
	}
	return stream;
}

/**
 * Remove .utf-8 keys in old BitComet torrent files.
 */
template <template <typename> class Tmpl>
basic_bencode<Tmpl> & utf8(basic_bencode<Tmpl> &r)
{
	typedef typename basic_bencode<Tmpl>::integer integer;
	typedef typename basic_bencode<Tmpl>::string string;
	typedef typename basic_bencode<Tmpl>::list list;
	typedef typename basic_bencode<Tmpl>::dictionary dictionary;

	if (list *v = r.template pointer<list>())
	{
		for (typename list::iterator i = v->begin(); i != v->end(); ++i)
			utf8(*i);
	}
	else if (dictionary *v = r.template pointer<dictionary>())
	{
		dictionary d;
		for (typename dictionary::iterator i = v->begin(); i != v->end(); ++i)
		{
			const typename dictionary::key_type &key = i->first;
			if (key.size() > 6 && key.substr(key.size() - 6) == ".utf-8")
				d[key.substr(0, key.size() - 6)] = i->second;
			else if (v->find(key + ".utf-8") == v->end())
			{
				utf8(i->second);
				d[key] = i->second;
			}
		}
		v->swap(d);
	}

	return r;
}

}} // namespace alpha::rpc
